#include "mode.h"
#include "main.h"

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "fatfs.h"
// #include "libjpeg.h"
#include "usb_device.h"
#include "lcd_init.h"
// #include "../lv_lib_png/lodepng.h"
#include <limits.h>

#define MAX_BMP_FILES 32      // 32
#define MAX_IMAGE_FILE_LEN 32 // 32
#define DEFAULT_IMAGE_CHANGE_INTERVAL 1000
// #define blockSize 8
#define linecount 30 // 20
#define GRamSize (linecount * LCD_Width * 3)
__attribute__((section(".ccmram"))) uint8_t lineData[GRamSize] = {0};

FRESULT res;
struct RA8876_Pixel_Mode2
{
    uint8_t b;
    uint8_t g;
    uint8_t r;
} __attribute__((packed));

typedef struct __attribute__((packed)) tagBITMAPFILEHEADER
{
    uint16_t bFileType;
    uint32_t bFileSize;
    uint32_t bReserved1;
    // uint16_t bReserved2;
    uint32_t bPixelDataOffset;
} BITMAPFILEHEADER; // 14bytes
/* __attribute__((packed))，它的作用就是告诉编译器：
取消结构体在编译过程中的优化对齐，按尽可能小的size对齐——也就是按1字节为单位对齐。*/

// typedef struct tagBITMAPFILEHEADER BITMAPFILEHEADER;

typedef struct __attribute__((packed)) tagBITMAPINFOHEADER
{
    uint32_t bHeaderSize;      // 图像信息头总大小（40bytes）
    uint32_t bImageWidth;      // 图像宽度（像素）
    uint32_t bImageHeight;     // 图像高度
    uint16_t bPlanes;          // 应该是0
    uint16_t bBitsPerPixel;    // 像素位数
    uint32_t bCompression;     // 图像压缩方法
    uint32_t bImageSize;       // 图像大小（字节）
    uint32_t bXpixelsPerMeter; // 横向每米像素数
    uint32_t bYpixelsPerMeter; // 纵向每米像素数
    uint32_t bTotalColors;     // 使用的颜色总数，如果像素位数大于8，则该字段没有意义
    uint32_t bImportantColors; // 重要颜色数，一般没什么用
} BITMAPINFOHEADER;            // 40byte

// typedef struct tagBITMAPINFOHEADER BITMAPINFOHEADER;

extern SD_HandleTypeDef hsd;
extern UART_HandleTypeDef huart1;
// extern SDRAM_HandleTypeDef hsram1;

const char Title[] = {" \r\n\
 _____            _     _   __\r\n\
(_   _)          ( )_ /  )  _  \\\r\n\
  | |    __   ___|  _)_  | ( ) |\r\n\
  | |  / __ \\  __) |   | | | | |\r\n\
  | | (  ___/__  \\ |_  | | (_) |\r\n\
  (_)  \\____)____/\\__) (_)\\___/\r\n\
   \r\n\
Copyright (c) zhaitao.as@outlook.com\r\n\
|||||||||||||||||||||||||||||||||||||\r\n\
\r\n\
"};

uint32_t testInterval = DEFAULT_IMAGE_CHANGE_INTERVAL;
FATFS fs;
FIL fil;
FRESULT fr;
uint32_t br;
uint8_t imageFileCount = 0;
char imageFileNames[MAX_BMP_FILES][MAX_IMAGE_FILE_LEN];
char *imageFileSorted[MAX_BMP_FILES];
// static uint8_t lineData[2048 * 3 * blockSize];

uint8_t rd;
uint8_t getUartFlag = 0;

/////////////////
uint32_t file_byte = 0;    // 文件大小（字节数）
uint32_t byteswritten = 0; // 写文件计数
uint32_t bytesread = 0;    // 读文件计数

uint8_t wtext[] = "THAT IS ME 4G CARD"; // 写的内容
uint8_t rtext[1024];                    // 读取的buff,1024bytes

char filename[] = "log.txt"; // 文件名
extern uint8_t retSD;        /* Return value for SD */
///////////////////

int mipiCheckIDDcs(void);
int mipiCheckIDGen(void);

/**
 * @brief get image (bmp, jpg) file name from sdcard
 *
 * @param path folder path
 * @param ifn image file names buffer
 * @return int file number
 */
int getSDCardImageFileNames(char *path, char *ifn[])
{
    FILINFO info;
    DIR file_dir;
    FRESULT fr;
    int cnt = 0;

    fr = f_opendir(&file_dir, path);
    if (fr)
    {
        printf("FATFS f_opendir ERROR\r\n");
        Error_Handler();
    }

    while (1)
    {
        fr = f_readdir(&file_dir, &info);

        if (fr || info.fname[0] == 0)
            break;
        if (info.fname[0] == '.')
            continue;
        if (strstr(info.fname, ".bmp") || strstr(info.fname, ".BMP"))
        {

            // only get file name in number
            char fileName[32];
            strcpy(fileName, info.fname);
            // printf("fileName ==== %s \r\n",fileName);
            char *pch;
            pch = strtok(fileName, ".");
            if (atoi(pch))
            {
                strcpy(ifn[cnt], info.fname);
                cnt++;
            }
        }
    }

    return cnt;
}

void sdcard_init(void)
{
    SDCardCheck(); ////Check sdcard

    // Get sdcard file names and sort
    do
    {
        for (int c = 0; c < MAX_BMP_FILES; c++)
        {
            imageFileSorted[c] = imageFileNames[c];
        }
        imageFileCount = getSDCardImageFileNames("", imageFileSorted);

        sortStrings(imageFileSorted, imageFileCount);

        printf("[LCMTEST] Sorted Image Files:\r\n");
        for (uint8_t i = 0; i < imageFileCount; i++)
        {
            printf("\t%s \n\r", imageFileSorted[i]);
            if ((i > 0) && (i % 8 == 0))
            {
                printf("\r\n");
            }
        }
    } while (0);
}

void SDCardCheck(void)
{
    int result = 0;

    //
    res = f_mount(&fs, "", 0);
    if (res)
    {
        printf("SD Check f_mount error %d\r\n", res);
        result += 1;
    }

    //
    res = f_open(&fil, "FatFs.txt", FA_OPEN_ALWAYS | FA_WRITE);
    if (res)
    {
        printf("SD Check1 f_open error %d\r\n", res);
        result += 1;
    }

    //
    uint8_t wtext[] = "SD Check: Read from write file SUCCESS\r\n";
    uint8_t rtext[100];
    uint32_t byteswritten;
    uint32_t bytesread;
    res = f_write(&fil, wtext, sizeof(wtext), (void *)&byteswritten);
    if (res)
    {
        printf("SD Check f_write error %d\r\n", res);
        result += 1;
    }

    //
    res = f_close(&fil);
    if (res)
    {
        printf("SD Check f_close error %d\r\n", res);
        result += 1;
    }

    //
    res = f_open(&fil, "FatFs.txt", FA_READ | FA_OPEN_EXISTING);
    if (res != FR_OK)
    {
        printf("SD Check2 f_open error %d\r\n", res);
        result += 1;
    }

    //
    res = f_read(&fil, rtext, 54, (UINT *)&bytesread);
    if (res != FR_OK)
    {
        printf("SD Check read error %d\r\n", res);
        result += 1;
    }

    //
    res = f_close(&fil);
    if (res)
    {
        printf("SD Check f_close error %d\r\n", res);
        result += 1;
    }

    // remove testing file
    res = f_unlink("Fatfs.txt");
    if (res)
    {
        printf("SD Check f_unlink error %d\r\n", res);
        result += 1;
    }

    if (result != 0)
    {
        printf("SDcard check failed\r\n");
        printf("SD Check write %d bytes, read %d bytes\r\n",
               (int)byteswritten, (int)bytesread);
        printf((const char *)rtext);

        Error_Handler();
    }
    else
    {
        printf("[SDcard] Check Success\r\n");
    }

    return;
}

uint32_t getBmpRawOffset(uint8_t *BMP_Header)
{
    /*uint32_t fileSize = 0;
    fileSize = ((uint32_t)(BMP_Header[5]) << 24) +
           ((uint32_t)(BMP_Header[4]) << 16) +
           ((uint32_t)(BMP_Header[3]) << 8) +
           ((uint32_t)(BMP_Header[2]) << 0);
    // printf("filesize: %d\r\n", fileSize);

    uint32_t rawDataSize = 0;
    rawDataSize = ((uint32_t)(BMP_Header[37]) << 24) +
              ((uint32_t)(BMP_Header[36]) << 16) +
              ((uint32_t)(BMP_Header[35]) << 8) +
              ((uint32_t)(BMP_Header[34]) << 0);
    // printf("rawDataSize: %d\r\n", rawDataSize);

    return fileSize - rawDataSize;*/
    return BMP_Header[2];
}

uint8_t bmpDrawFile(char *fileName)
{

    int __error = 0;
    // static FIL fil;
    uint8_t BMP_Header[54];
    uint16_t width, height;
    volatile uint16_t x;
    volatile struct RA8876_Pixel_Mode2 *pixel;

    printf("bmpDrawFile: %s \r\n", fileName);

    res = f_open(&fil, (const TCHAR *)fileName, FA_READ | FA_OPEN_EXISTING);
    if (res != FR_OK)
    {
        printf("bmpDrawFile f_open error %d\r\n", res);
        __error = 2;
        goto bmpDrawFile_close;
    }

    //
    res = f_read(&fil, BMP_Header, 54, (UINT *)&br);
    if (res != FR_OK)
    {
        printf("bmpDrawFile file read error\r\n");
        __error = 3;
        goto bmpDrawFile_close;
    }

    BITMAPFILEHEADER *fh = (BITMAPFILEHEADER *)BMP_Header;        // 前14字节
    BITMAPINFOHEADER *ih = (BITMAPINFOHEADER *)(BMP_Header + 14); // 54-14=40字节

    if (fh->bFileType != 0x4D42)
    {
        printf("bmpDrawFile file type should 0x4D42, get: %04x\r\n", fh->bFileType);
        __error = 1;
        goto bmpDrawFile_close;
    }

    width = ih->bImageWidth;
    height = ih->bImageHeight;
    uint32_t lineDataSize = width * 3; /* width * 3, In Byte */

    if (lineDataSize % 4)
    { // BMP格式，170*3除4，有余数，自动补了两个字节；
        lineDataSize += 4 - lineDataSize % 4;
    }
    uint32_t filesize = fh->bPixelDataOffset + lineDataSize * height;
    uint32_t lineCountGRamCache = GRamSize / lineDataSize;

    static uint32_t lineStartPosition = 0;
    static uint32_t br = 0;
    uint32_t lineDrawed = 0;
    uint32_t lineToCache = 0;
    uint32_t dataDrawed = 0;
    uint32_t dataToDraw = 0;
    ////wr_c2c();

    wr_cmd_parall1_8(0x2c);
    // Write(0,0x0022);
    while (lineDrawed < height)
    {

        /* Calculate cache size */
        if ((lineDrawed + lineCountGRamCache) < height)
        {
            // 没到图片结尾, 缓存gram所能容纳的最大行数
            lineToCache = lineCountGRamCache;
        }
        else
        {
            // 到了结尾, 只需要缓存一部分行
            lineToCache = height - lineDrawed;
        }

        /* 	Calculate new cache area start position
            bmp图片是y翻转存放的, 文件头之后是图片最后一行, 文件结尾是图片的第一行
            为了保准观感上刷图是从屏的0行开始, 要先从文件结尾部分开始刷
            先找到文件结尾, 再减去已经刷过的行, 再减去这一次要刷的行, 就是新的起点
        */
        dataDrawed = lineDrawed * lineDataSize;
        dataToDraw = lineToCache * lineDataSize;
        br = filesize - dataDrawed - dataToDraw;

        /* Move Pointer to position of new cache */
        if (f_lseek(&fil, br))
        {
            printf("bmpDrawFile f_lseek error\r\n");
            __error = 3;
            goto bmpDrawFile_close;
        }

        /* Read Line Data */
        if (f_read(&fil, lineData, dataToDraw, (UINT *)&br))
        {
            printf("bmpDrawFile f_read error\r\n");
            __error = 4;
            goto bmpDrawFile_close;
        }
        /* Draw cached data */
        // uint8_t color1,color2,color3;
        for (uint32_t i = 0; i < lineToCache; i++)
        {
            lineStartPosition = (lineToCache - 1 - i) * lineDataSize;
            pixel = (struct RA8876_Pixel_Mode2 *)(lineData + lineStartPosition);

/* Draw one line of cached*/
#if (SPI_565)
            {
                for (x = 0; x < width; x++)
                {
                    uint8_t color1 = (pixel->r & 0xf8) | ((pixel->g & 0xfc) >> 5); // 16bit
                    uint8_t color2 = ((pixel->g & 0xfc) << 3) | ((pixel->b & 0xf8) >> 3);
                    // printf("pixel->r = %ld,pixel->g = %ld,pixel->b = %ld\n\r",pixel->r,pixel->g,pixel->b);
                    wr_num(color1);
                    wr_num(color2);
                    pixel++;
                }
            }
#endif
#if (Parallel2_18bit)
            {
                for (x = 0; x < width; x++)
                {
                    uint16_t color_H = (pixel->r & 0xc0) >> 6; // 18bit-取高位
                    // uint16_t color_L = ((pixel->r & 0x3c) <<10) | ((pixel->g & 0xfc)<<4) | ((pixel->b & 0xfc)>>2);
                    uint16_t color_L =
                        (((pixel->r & 0x3c) << 10) & 0b1111000000000000) |
                        (((pixel->g & 0xfc) << 04) & 0b0000111111000000) |
                        (((pixel->b & 0xfc) >> 02) & 0b0000000000111111);
                    wr_displ_parall2_18(color_H, color_L);
                    pixel++;
                }
            }
#endif
#if (Parallel1_8bit)
            {
                for (x = 0; x < width; x++)
                {
                    wr_dat_parall1_8(pixel->r);
                    wr_dat_parall1_8(pixel->g);
                    wr_dat_parall1_8(pixel->b);
                    //uint16_t colorH, colorL;
                    //colorH = ((pixel->r & 0xFC) << 1) | ((pixel->g & 0xE0) >> 5);
                    //colorL = ((pixel->g & 0x1C) << 4) | ((pixel->b & 0xFC) >> 2);
                    //wr_dat_parall1_8(colorH);
                    //wr_dat_parall1_8(colorL);
                    pixel++;
                }
            }
#endif
#if (Parallel1_16bit)
            {
                for (x = 0; x < width; x= x+2)
                {
                    //uint16_t color_1 = 0;
                    //uint16_t color_2 = 0;
                    //uint16_t color_3 = 0;
                    //color_1 = (((pixel->r) << 8) & 0xFF00) | ((pixel->g) & 0xFF);
                    //color_2 = ((pixel->b) << 8) & 0xFF00;
                    //pixel++;
                    //color_2 = color_2 | ((pixel->r) & 0xFF);
                    //color_3 = (((pixel->g) << 8) & 0xFF00) | ((pixel->b) & 0xFF);
                    //wr_dat_parall1_16(color_1);
                    //wr_dat_parall1_16(color_2);
                    //wr_dat_parall1_16(color_3);
                    //pixel++;
                    
                    uint16_t color1 = (((pixel->r) << 8) & 0xFC00) | ((pixel->g) & 0xFC);
                    uint16_t color21 = ((pixel->b) << 8) & 0xFC00;
                    pixel++;
                    uint16_t color2 = color21 | (pixel->r & 0xFC);
                    uint16_t color3 = (((pixel->g) << 8) & 0xFC00) | ((pixel->b) & 0xFC);
                    wr_dat_parall1_16(color1);
                    wr_dat_parall1_16(color2);
                    wr_dat_parall1_16(color3);
                    pixel++;
                }
            }
#endif
        }
        ////HAL_GPIO_WritePin(ULED2_GPIO_Port, ULED2_Pin, GPIO_PIN_SET);

        lineDrawed += lineToCache;
    }

bmpDrawFile_close:

    if (f_close(&fil))
    {
        printf("bmpDrawFile f_close error\r\n");
    }

    return __error;
}
uint8_t drawFile(char *fn)
{
    uint8_t r = 0;

    // HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);

    if (strstr(fn, ".bmp") || strstr(fn, ".BMP"))
    {
        bmpDrawFile(fn);
    }
    else if (strstr(fn, ".jpg"))
    {
        // jpgDrawFile(fn);
    }
    else
    {
        r = -1;
    }

    // HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);

    return r;
}

/**
 * @retval None
 */

/************************************************************************************************
 *
 *  								Functions Export to Lua
 *
 ***********************************************************************************************/
/* @brief sort string
 *
 * @param strings
 * @param num
 */
void sortStrings(char *strings[], int num)
{
    char *temp;
    int top, seek;

    for (top = 0; top < num - 1; top++)
    {
        for (seek = top + 1; seek < num; seek++)
        {
            // if (strcmp(strings[top],strings[seek]) > 0)
            if (atoi(strings[top]) - atoi(strings[seek]) > 0)
            {
                temp = strings[top];
                strings[top] = strings[seek];
                strings[seek] = temp;
            }
        }
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    UNUSED(huart);
    // HAL_UART_Transmit(&huart1, &rd, 1, 0xfff);
    getUartFlag = 1;
    // HAL_UART_Receive_IT(&huart1, &rd, 1);
}

int testModeStart(void)
{
    int result = 0;
    SDCardCheck(); ////Check sdcard

    // Get sdcard file names and sort
    do
    {
        for (int c = 0; c < MAX_BMP_FILES; c++)
        {
            imageFileSorted[c] = imageFileNames[c];
        }
        imageFileCount = getSDCardImageFileNames("", imageFileSorted);

        sortStrings(imageFileSorted, imageFileCount);

        printf("[LCMTEST] Sorted Image Files:\r\n");
        for (uint8_t i = 0; i < imageFileCount; i++)
        {
            printf("\t%s ", imageFileSorted[i]);
            if ((i > 0) && (i % 8 == 0))
            {
                printf("\r\n");
            }
        }
    } while (0);

    // Main Loop With Key Pause
    uint8_t pauseFlag = 0;
    uint32_t timePiece = testInterval + 1;
    uint8_t imageIndex = 0;
    ////HAL_UART_Receive_IT(&huart1, &rd, 1);
    while (1)
    {
        // Detect Key State, and Change pauseFlag
        if (HAL_GPIO_ReadPin(PAUSE_PA5_GPIO_Port, PAUSE_PA5_Pin) == GPIO_PIN_RESET)
        {
            HAL_Delay(20);
            while (HAL_GPIO_ReadPin(PAUSE_PA5_GPIO_Port, PAUSE_PA5_Pin) == GPIO_PIN_RESET)
            {
            };
            // printf("HAS COUNT \r\n");
            if (pauseFlag == 0)
            {
                pauseFlag = 1;
                timePiece = 0;
                // HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
            }
            else
            {
                pauseFlag = 0;
                // HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
                timePiece = testInterval + 1; // change image immediately
            }
        }
        // Detect pause flag, if flag, timePiece++, else keep remain
        if (pauseFlag == 0)
        {
            timePiece++;
        }

        // Detect timePiece, if timePiece >= 1000, change display image,and make timePiece 0//
        if (timePiece >= testInterval)
        {
            timePiece = 0;

            if (imageIndex >= imageFileCount)
            {
                imageIndex = 0;
            }
            // printf("imageIndex  == %d ,testInterval  == %d \n\r ",imageIndex,testInterval);
            drawFile(imageFileSorted[imageIndex]);
            imageIndex++;
        }

        // time piece interval = 1ms
        HAL_Delay(1);
    }
    return result;
}
